using System;
using System.Reflection;
using System.Collections;


namespace EventBroker
{
        
	/// <summary>
	/// Inspects objects for event m_aPublications and m_aSubscriptions,
	/// registering and unregistering the publishers and subscribers with the corresponding eventTopic_p
	/// </summary>
	public class CEventManager
	{
        private Hashtable m_htEventTopicToTopicTable;
        private  Hashtable m_htWorkItemGroupNameToWorkItemGroupID;
        private int m_nWorkItemGroupID = 0;
        public static CEventManager s_eventManager = null;

        public int WorkItemGroupID
        {
            get
            {
                return m_nWorkItemGroupID;
            }

            set
            {
                m_nWorkItemGroupID = value;
            }
 
        }

        public Hashtable WorkItemGroupNameToWorkItemGroup
        {
            get
            {
                return m_htWorkItemGroupNameToWorkItemGroupID;
            }
        }

        

        private CEventManager()
        {
            m_htEventTopicToTopicTable = new Hashtable();
            m_htWorkItemGroupNameToWorkItemGroupID = new Hashtable();
        }

        public static CEventManager GetInstance()
        {
            if (s_eventManager == null)
            {
                s_eventManager = new CEventManager();
                return s_eventManager;
            }
            else
            {
                return s_eventManager;   
            }
        }

        public CEventTopic GetEventTopic(string strTopic_p)
        {
            CEventTopic eventTopic = FindEventTopic(strTopic_p);

            if (eventTopic == null)
            {
                eventTopic = new CEventTopic(strTopic_p);
                m_htEventTopicToTopicTable.Add(strTopic_p, eventTopic);
                return eventTopic;
            }
            else
            {
                return eventTopic;
            }
        }

        private CEventTopic FindEventTopic(string strTopic_p)
        {
            if (m_htEventTopicToTopicTable.Contains(strTopic_p) == true)
            {
                return (CEventTopic)m_htEventTopicToTopicTable[strTopic_p];
            }
            else
            {
                return null;
            }

        }


        private int FindWorkItemGroupID(string strWorkItemGroupName_p)
        {
            if (m_htWorkItemGroupNameToWorkItemGroupID.Contains(strWorkItemGroupName_p) == true)
            {
                return (int)m_htWorkItemGroupNameToWorkItemGroupID[strWorkItemGroupName_p];
            }
            else
            {
                return 0;
            }

        }

		/// <summary>
		/// Processes an object by scanning its members and registering m_aPublications 
		/// and m_aSubscriptions.
		/// </summary>
	    public static void Register(object objItem_p)
		{
            ProcessPublishers(objItem_p, objItem_p.GetType(), true);
            ProcessSubscribers(objItem_p, objItem_p.GetType(), true);
		}

		/// <summary>
		/// Processes an object by scanning its members and unregistering m_aPublications 
		/// and m_aSubscriptions.
		/// </summary>
        public static void Unregister(object objItem_p)
		{
            ProcessPublishers(objItem_p, objItem_p.GetType(), false);
            ProcessSubscribers(objItem_p, objItem_p.GetType(), false);
		}

        private static void ProcessPublishers(object objItem_p,
                                              Type itemType_p, 
                                              bool bRegister_p)
		{
            foreach (EventInfo info in itemType_p.GetEvents())
			{
                foreach (CEventPublicationAttribute attrEventPublication in info.GetCustomAttributes(typeof(CEventPublicationAttribute), true))
				{
                    HandlePublisher(objItem_p, bRegister_p, info, attrEventPublication);
				}
			}
		}


        private static void HandlePublisher(object objItem_p,
                                            bool bRegister_p, 
                                            EventInfo eiInfo_p, 
                                            CEventPublicationAttribute attrEventPublication_p)
		{
            CEventTopic eventTopic = CEventManager.GetInstance().GetEventTopic(attrEventPublication_p.Topic);
            if (bRegister_p)
			{
                eventTopic.AddPublication(objItem_p, eiInfo_p.Name);
			}
			else
			{
                eventTopic.RemovePublication(objItem_p, eiInfo_p.Name);
			}
		}


        private static void ProcessSubscribers(object objItem_p, Type itemType_p, bool bRegister_p)
		{
            foreach (MethodInfo info in itemType_p.GetMethods())
			{
                foreach (CEventSubscriptionAttribute attrEventSubscription in info.GetCustomAttributes(typeof(CEventSubscriptionAttribute), true))
				{
                    HandleSubscriber(objItem_p, bRegister_p, info, attrEventSubscription);
				}
			}
		}

        private static void HandleSubscriber(object objItem_p,
                                             bool bRegister_p,
                                             MethodInfo info_p,
                                             CEventSubscriptionAttribute attrEventPublication_p)
		{
            int nSelectedWorkItemGroupID = 0;

            CEventTopic eventTopic = CEventManager.GetInstance().GetEventTopic(attrEventPublication_p.Topic);
            if (bRegister_p == true)
			{
                Type[] aParamTypes = GetParamTypes(info_p);

                if(attrEventPublication_p.WorkItemGroupName != null)
                {
                    nSelectedWorkItemGroupID = CEventManager.GetInstance().FindWorkItemGroupID(attrEventPublication_p.WorkItemGroupName);

                    if (nSelectedWorkItemGroupID == 0)
                    {
                        CEventManager.GetInstance().WorkItemGroupNameToWorkItemGroup.Add(attrEventPublication_p.WorkItemGroupName, ++CEventManager.GetInstance().WorkItemGroupID);
                        nSelectedWorkItemGroupID = CEventManager.GetInstance().WorkItemGroupID;
                    }
                   
                    eventTopic.AddSubscription(objItem_p, info_p.Name, aParamTypes, attrEventPublication_p.Thread, nSelectedWorkItemGroupID);
                }
                else
                {
                    //error.
                }
			}
			else
			{
                eventTopic.RemoveSubscription(objItem_p, info_p.Name);
			}
		}

		private static Type[] GetParamTypes(MethodInfo methodInfo_p)
		{
            ParameterInfo[] aParamInfos = methodInfo_p.GetParameters();
            Type[] aParamTypes = new Type[aParamInfos.Length];
            for (int i = 0; i < aParamTypes.Length; i++)
			{
                aParamTypes[i] = aParamInfos[i].ParameterType;
			}
            return aParamTypes;
		}
	}
}